package com.zzsz.utils;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.List;
import java.util.function.Function;

/**
 * @Classname ExcelExporter
 * @Description
 * @Date 2025/9/5 16:05
 * @Created by SYL
 */


public class ExcelExportUtil {
    public static <T> void exportExcel(
            HttpServletResponse response,
            List<T> dataList,
            String[] headers,
            Function<T, Object>[] fieldExtractors,
            String fileName) {

        if (dataList == null || dataList.isEmpty()) {
            writeErrorResponse(response, "无数据可导出");
            return;
        }

        SXSSFWorkbook workbook = null;
        OutputStream out = null;

        try {
            // 1. 创建工作簿（SXSSFWorkbook处理大数据量）
            workbook = new SXSSFWorkbook(1000); // 内存中保留1000行，其余写入临时文件
            Sheet sheet = workbook.createSheet(fileName);

            // 2. 创建表头样式
            CellStyle headerStyle = createHeaderStyle(workbook);

            // 3. 写入表头
            Row headerRow = sheet.createRow(0);
            for (int i = 0; i < headers.length; i++) {
                Cell cell = headerRow.createCell(i);
                cell.setCellValue(headers[i]);
                cell.setCellStyle(headerStyle);
                sheet.setColumnWidth(i, 20 * 256); // 统一列宽
            }

            // 4. 写入数据行
            for (int i = 0; i < dataList.size(); i++) {
                T item = dataList.get(i);
                Row dataRow = sheet.createRow(i + 1);
                for (int j = 0; j < fieldExtractors.length; j++) {
                    Object value = fieldExtractors[j].apply(item); // 调用字段提取器获取值
                    Cell cell = dataRow.createCell(j);
                    setCellValue(cell, value); // 自动处理不同类型值
                }
            }

            // 5. 设置响应头并下载
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("UTF-8");
            String encodedFileName = URLEncoder.encode(fileName + "_" + System.currentTimeMillis(), "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + encodedFileName + ".xlsx");

            // 6. 写入响应流
            out = response.getOutputStream();
            workbook.write(out);
            out.flush();

        } catch (Exception e) {
            writeErrorResponse(response, "导出失败：" + e.getMessage());
            e.printStackTrace();
        } finally {
            // 7. 清理资源
            if (workbook != null) {
                try {
                    workbook.dispose(); // 清理SXSSF临时文件
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 基于字段名的Excel导出方法
     * @param response HTTP响应对象
     * @param dataList 数据列表
     * @param headers 表头数组
     * @param fieldNames 字段名数组
     * @param fileName 文件名
     * @param <T> 数据类型
     */
    public static <T> void exportExcel(
            HttpServletResponse response,
            List<T> dataList,
            String[] headers,
            String[] fieldNames,
            String fileName) {

        if (dataList == null || dataList.isEmpty()) {
            writeErrorResponse(response, "无数据可导出");
            return;
        }

        SXSSFWorkbook workbook = null;
        OutputStream out = null;

        try {
            // 1. 创建工作簿（SXSSFWorkbook处理大数据量）
            workbook = new SXSSFWorkbook(1000); // 内存中保留1000行，其余写入临时文件
            Sheet sheet = workbook.createSheet(fileName);

            // 2. 创建表头样式
            CellStyle headerStyle = createHeaderStyle(workbook);

            // 3. 写入表头
            Row headerRow = sheet.createRow(0);
            for (int i = 0; i < headers.length; i++) {
                Cell cell = headerRow.createCell(i);
                cell.setCellValue(headers[i]);
                cell.setCellStyle(headerStyle);
                sheet.setColumnWidth(i, 20 * 256); // 统一列宽
            }

            // 4. 获取数据类的Class对象
            Class<?> dataClass = dataList.get(0).getClass();

            // 5. 写入数据行
            for (int i = 0; i < dataList.size(); i++) {
                T item = dataList.get(i);
                Row dataRow = sheet.createRow(i + 1);
                for (int j = 0; j < fieldNames.length; j++) {
                    // 通过反射获取字段值
                    Object value = getFieldValue(item, dataClass, fieldNames[j]);
                    Cell cell = dataRow.createCell(j);
                    setCellValue(cell, value); // 自动处理不同类型值
                }
            }

            // 6. 设置响应头并下载
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("UTF-8");
            String encodedFileName = URLEncoder.encode(fileName + "_" + System.currentTimeMillis(), "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + encodedFileName + ".xlsx");

            // 7. 写入响应流
            out = response.getOutputStream();
            workbook.write(out);
            out.flush();

        } catch (Exception e) {
            writeErrorResponse(response, "导出失败：" + e.getMessage());
            e.printStackTrace();
        } finally {
            // 8. 清理资源
            if (workbook != null) {
                try {
                    workbook.dispose(); // 清理SXSSF临时文件
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            // 确保响应已经完成
            try {
                response.flushBuffer();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // 创建表头样式（加粗、居中）
    private static CellStyle createHeaderStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setBold(true);
        style.setFont(font);
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        return style;
    }

    // 根据值类型设置单元格内容（处理null和不同数据类型）
    private static void setCellValue(Cell cell, Object value) {
        if (value == null) {
            cell.setCellValue("");
            return;
        }
        if (value instanceof String) {
            cell.setCellValue((String) value);
        } else if (value instanceof Number) {
            cell.setCellValue(((Number) value).doubleValue());
        } else if (value instanceof Boolean) {
            cell.setCellValue((Boolean) value);
        } else {
            cell.setCellValue(value.toString());
        }
    }

    // 向前端写入错误信息
    public static void writeErrorResponse(HttpServletResponse response, String message) {
        try {
            response.reset();
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().write(message);
            response.flushBuffer();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过反射获取对象字段值
     * @param obj 对象实例
     * @param clazz 对象类
     * @param fieldName 字段名
     * @return 字段值
     */
    private static Object getFieldValue(Object obj, Class<?> clazz, String fieldName) {
        try {
            Field field = getField(clazz, fieldName);
            if (field != null) {
                field.setAccessible(true);
                return field.get(obj);
            }
            return null;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 递归获取类及其父类中的字段
     * @param clazz 类对象
     * @param fieldName 字段名
     * @return 字段对象
     */
    private static Field getField(Class<?> clazz, String fieldName) {
        while (clazz != null) {
            try {
                return clazz.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e) {
                clazz = clazz.getSuperclass();
            }
        }
        return null;
    }
}
